/*
 * @(#)AbstractExecutableMemberWriter.java	1.50 04/07/27
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package org.joe_e.doclets.html;

import org.joe_e.doclets.internal.toolkit.util.Util;
import org.joe_e.doclets.MemberInfo;

import com.sun.javadoc.*;

/**
 * Print method and constructor info.
 *
 * @author Robert Field
 * @author Atul M Dambalkar
 * @author Akshay Krishnamurthy
 * @author Kanav Arora
 */
public abstract class AbstractExecutableMemberWriter extends AbstractMemberWriter {

    public AbstractExecutableMemberWriter(SubWriterHolderWriter writer,
                                     ClassDoc classdoc) {
        super(writer, classdoc);
    }

    public AbstractExecutableMemberWriter(SubWriterHolderWriter writer) {
        super(writer);
    }
    
    /**
     * Write the type parameters for the executable member.
     * 
     * @param member the member to write type parameters for.
     * @return the display length required to write this information.
     */
    protected int writeTypeParameters(ExecutableMemberDoc member) {
        LinkInfoImpl linkInfo = new LinkInfoImpl(
            LinkInfoImpl.CONTEXT_MEMBER_TYPE_PARAMS, member, false);
        String typeParameters = writer.getTypeParameterLinks(linkInfo);      
        if (linkInfo.displayLength > 0) {
            writer.print(typeParameters + " ");
            writer.displayLength += linkInfo.displayLength + 1;
        }
        return linkInfo.displayLength;
    }

    protected void writeSignature(ExecutableMemberDoc member) {
        writer.displayLength = 0;
        writer.pre();
        writer.writeAnnotationInfo(member);
        printModifiers(member);
        writeTypeParameters(member);
        if (configuration().linksource &&
            member.position().line() != classdoc.position().line()) {
            writer.printSrcLink(member, member.name());
        } else {
            bold(member.name());
        }
        writeParameters(member);
        writeExceptions(member);
        writer.preEnd();
    }
      
    protected void writeDeprecatedLink(ProgramElementDoc member) {
        ExecutableMemberDoc emd = (ExecutableMemberDoc)member;
        writer.printDocLink(LinkInfoImpl.CONTEXT_MEMBER, (MemberDoc) emd, 
            emd.qualifiedName() + emd.flatSignature(), false);
    }
  
    protected void writeSummaryLink(int context, ClassDoc cd, ProgramElementDoc member) {
        ExecutableMemberDoc emd = (ExecutableMemberDoc)member;
        String name = emd.name();
        writer.bold();
        writer.printDocLink(context, cd, (MemberDoc) emd, 
            name, false);
        writer.boldEnd();
        writer.displayLength = name.length();
        writeParameters(emd, false);
    }
 
    protected void writeInheritedSummaryLink(ClassDoc cd,
            ProgramElementDoc member) {
        MethodDoc method = (MethodDoc) member;
    	MemberInfo m = configuration().consumer.getInfo(method);
    	if (m == null || !m.enabled) {
    		if (method.isIncluded() || Util.isLinkable(cd, configuration())) {
    			String basepath = "";
    			int count = configuration().currentcd.qualifiedName().split("\\.").length - 1;
    			for (; count > 0; count--) {
    				basepath += "../";
    			}
    			basepath += cd.qualifiedName().replace('.', '/') + ".html";
    			basepath += "#" + method.name() + method.signature();
    			print("<a style=\"color: #C0C0C0; visited: #C0C0C0\" href=\"" + basepath + "\">" + (String) method.name() + "</a>");
    		}
    		else {
    			print("<font color='#C0C0C0'>" + method.name() + "</font>");
    		}
    	}
    	else {
    		writer.printDocLink(LinkInfoImpl.CONTEXT_MEMBER, cd, (MemberDoc) member, 
    				member.name(), false);
    	}
    }

    protected void writeParam(ExecutableMemberDoc member, Parameter param, 
        boolean isVarArg) {
        if (param.type() != null) {
            writer.printLink(new LinkInfoImpl(
                LinkInfoImpl.CONTEXT_EXECUTABLE_MEMBER_PARAM, param.type(), 
                isVarArg));
        }
        if(param.name().length() > 0) {
            writer.space();
            writer.print(param.name());
        }
    }
    
    protected void writeParameters(ExecutableMemberDoc member) {
    	writeParameters(member, true);        
    }

    protected void writeParameters(ExecutableMemberDoc member, 
            boolean includeAnnotations) {
        print('(');
        Parameter[] params = member.parameters();
        String indent = makeSpace(writer.displayLength);
        if (configuration().linksource) {
            //add spaces to offset indentation changes caused by link.
            indent+= makeSpace(member.name().length());
        }
        int paramstart;
        for (paramstart = 0; paramstart < params.length; paramstart++) {
            Parameter param = params[paramstart];
            if (!param.name().startsWith("this$")) {
                if (includeAnnotations) {
                	boolean foundAnnotations = 
                		writer.writeAnnotationInfo(indent.length(), member, param);
                	if (foundAnnotations) {
                		writer.println();
                		writer.print(indent);
                    }
                }
                writeParam(member, param, 
                    (paramstart == params.length - 1) && member.isVarArgs());
                break;
            }
        }

        for (int i = paramstart + 1; i < params.length; i++) {
            writer.print(',');
            writer.println();
            writer.print(indent);
            if (includeAnnotations) {
                boolean foundAnnotations = 
                    writer.writeAnnotationInfo(indent.length(), member, params[i]);
                if (foundAnnotations) {
                    writer.println();
                    writer.print(indent);
                }
            }
            writeParam(member, params[i], (i == params.length - 1) && member.isVarArgs());
        }
        writer.print(')');
    }

    protected void writeExceptions(ExecutableMemberDoc member) {
        Type[] exceptions = member.thrownExceptionTypes();
        if(exceptions.length > 0) {
            LinkInfoImpl memberTypeParam = new LinkInfoImpl(
                LinkInfoImpl.CONTEXT_MEMBER, member, false);
            int retlen = getReturnTypeLength(member);
            writer.getTypeParameterLinks(memberTypeParam);
            retlen += memberTypeParam.displayLength == 0 ?
                0 : memberTypeParam.displayLength + 1;
            String indent = makeSpace(modifierString(member).length() +
                member.name().length() + retlen - 4);
            writer.println();
            writer.print(indent);
            writer.print("throws ");
            indent += "       ";
            writer.printLink(new LinkInfoImpl(
                LinkInfoImpl.CONTEXT_MEMBER, exceptions[0]));
            for(int i = 1; i < exceptions.length; i++) {
                writer.println(",");
                writer.print(indent);
                writer.printLink(new LinkInfoImpl(
                    LinkInfoImpl.CONTEXT_MEMBER, exceptions[i]));
            }
        }
    }

    protected int getReturnTypeLength(ExecutableMemberDoc member) {
        if (member instanceof MethodDoc) {
            MethodDoc method = (MethodDoc)member;
            Type rettype = method.returnType();            
            if (rettype.isPrimitive()) {
                return rettype.typeName().length() +
                       rettype.dimension().length();
            } else {
                LinkInfoImpl linkInfo = new LinkInfoImpl(
                    LinkInfoImpl.CONTEXT_MEMBER, rettype);
                writer.getLink(linkInfo);
                return linkInfo.displayLength;
            }
        } else {   // it's a constructordoc
            return -1;
        }
    }

    protected ClassDoc implementsMethodInIntfac(MethodDoc method,
                                                ClassDoc[] intfacs) {
        for (int i = 0; i < intfacs.length; i++) {
            MethodDoc[] methods = intfacs[i].methods();
            if (methods.length > 0) {
                for (int j = 0; j < methods.length; j++) {
                    if (methods[j].name().equals(method.name()) &&
                          methods[j].signature().equals(method.signature())) {
                        return intfacs[i];
                    }
                }
            }
        }
        return null;
    }
    
    /**
     * For backward compatibility, include an anchor using the erasures of the
     * parameters.  NOTE:  We won't need this method anymore after we fix 
     * see tags so that they use the type instead of the erasure.
     * 
     * @param emd the ExecutableMemberDoc to anchor to.
     * @return the 1.4.x style anchor for the ExecutableMemberDoc.
     */
    protected String getErasureAnchor(ExecutableMemberDoc emd) {
        StringBuffer buf = new StringBuffer(emd.name() + "(");
        Parameter[] params = emd.parameters();
        boolean foundTypeVariable = false;
        for (int i = 0; i < params.length; i++) {
            if (i > 0) {
                buf.append(",");
            }
            Type t = params[i].type();
            foundTypeVariable = foundTypeVariable || t.asTypeVariable() != null;
            buf.append(t.isPrimitive() ? 
                t.typeName() : t.asClassDoc().qualifiedName());            
        }
        buf.append(")");
        return foundTypeVariable ? buf.toString() : null;
    }
}
